#include "DeclarationManager.h"
#include "..\utils\FileUtils.h"
#include "..\utils\Log.h"
#include "LayerParser.h"
#include "ShaderParser.h"
#include "MenuComponentParser.h"
#include "ScreenQuadParser.h"
#include "TextureParser.h"
#include <winbase.h>
#include "..\particles\ParticleSystem.h"
#include "..\particles\ParticleSystemDecl.h"
#include <algorithm>
#include "SoundParser.h"
#include "RenderTargetParser.h"
#include "FontParser.h"

namespace ds {

bool declarationTypeSort(ResourceDeclaration* decl1,ResourceDeclaration* recl2) { 
	return decl1->getType() < recl2->getType();
}

DeclarationManager::DeclarationManager(float reloadDelay) : m_ReloadDelay(reloadDelay) , m_Timer(0.0f) {
	m_Parsers.push_back(new LayerParser());
	m_Parsers.push_back(new RenderTargetParser());
	m_Parsers.push_back(new ShaderParser());
	m_Parsers.push_back(new ScreenQuadParser());	
	m_Parsers.push_back(new TextureParser());
	m_Parsers.push_back(new FontParser());
	m_Parsers.push_back(new SoundParser());
	m_Parsers.push_back(new ParticleSystemDeclParser());
	m_Parsers.push_back(new MenuComponentParser());
}

DeclarationManager::~DeclarationManager() {
}

ResourceDeclaration* DeclarationManager::getDeclaration(const std::string& name) {
	for ( size_t i = 0; i < m_Declarations.size(); ++i ) {
		if ( m_Declarations[i]->getName() == name ) {
			return m_Declarations[i];
		}
	}
	return 0;
}

void DeclarationManager::checkFiles(float elapsed) {
	m_Timer += elapsed;
	if ( m_Timer > m_ReloadDelay ) {
		m_Timer = 0.0f;
		for ( size_t i = 0; i < m_Files.size();++i ) {
			ResourceFile* rf = &m_Files[i];
			FILETIME* ft = FileUtils::getFileTime(rf->fqName);
			if ( CompareFileTime(rf->fileTime,ft) == -1 ) {
				LOGC(logINFO,"DeclManager") << "file has changed -> reloading " << rf->fqName;
				LOGC(logINFO,"DeclManager") << "declaration type " << rf->declarationType;
				DeclarationParser* parser = getParserByType(rf->declarationType);
				if ( parser != 0 ) {
					parser->loadTextFile(*rf);
				}
				rf->fileTime = ft;
			}
		}
	}
}

void DeclarationManager::processDirectory(const std::string& dir) {
	std::vector<ResourceFile> files;
	scanDirectories(dir,files);
	LOGC(logINFO,"DeclManager") << "scanDirectory " << dir << " number of files: " << files.size();
	for ( size_t i = 0; i < files.size(); ++i ) {
		ResourceFile* rf = &files[i];		
		DeclarationParser* parser = getParserByEnding(rf->ending);
		if ( parser != 0 ) {
			LOGC(logINFO,"DeclManager") << "found parser - parsing file " << rf->fqName;
			rf->declarationType = parser->getType();
			if ( parser->isReloadable() ) {
				LOGC(logINFO,"DeclManager") << "file is reloadable - adding it to internal list";
				m_Files.push_back(*rf);
			}
			else {
				LOGC(logINFO,"DeclManager") << "file is NOT reloading - just parsing";
			}
			ResourceDeclaration* rd = parser->loadTextFile(*rf);
			if ( rd != 0 ) {
				m_Declarations.push_back(rd);
			}
			else {
				LOGC(logINFO,"DeclManager") << "The parser can handle multiple resources - loading all now";
				parser->loadTextFile(*rf,this);
			}
		}	
		else {
			LOGC(logINFO,"DeclManager") << "no matching parser found for " << rf->ending;
		}
	}			
	LOGC(logINFO,"DeclManager") << "number of declarations " << m_Declarations.size();
}

void DeclarationManager::createResources() {
	LOGC(logINFO,"DeclManager") << "creating all resources";
	std::sort(m_Declarations.begin(),m_Declarations.end(),declarationTypeSort);
	for ( size_t i = 0; i < m_Declarations.size(); ++i ) {
		DeclarationParser* parser = getParserByType(m_Declarations[i]->getType());
		parser->createResource(m_Declarations[i]);
	}
}

DeclarationParser* DeclarationManager::getParserByType(DeclarationType type) {
	for ( size_t j = 0; j < m_Parsers.size(); ++j ) {
		DeclarationParser* parser = m_Parsers[j];
		if ( parser->getType() == type ) {
			return parser;
		}
	}
	return 0;
}

DeclarationParser* DeclarationManager::getParserByEnding(const std::string& ending) {
	for ( size_t j = 0; j < m_Parsers.size(); ++j ) {
		DeclarationParser* parser = m_Parsers[j];
		if ( parser->isSupported(ending) ) {
			return parser;
		}
	}
	return 0;
}

void DeclarationManager::scanDirectories(const std::string& baseDir,std::vector<ResourceFile>& files) {
	LOGC(logINFO,"DelcManager") << "scanning directory '" << baseDir << "'";
	WIN32_FIND_DATAA ffd;	
	std::string dir = baseDir + "\\*.*";
	HANDLE hFind = FindFirstFile(dir.c_str(), &ffd);
	if ( INVALID_HANDLE_VALUE != hFind ) {      
		do {
			if ( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {         
				if (strcmp(ffd.cFileName, ".") != 0 && strcmp(ffd.cFileName, "..") != 0) {
					scanDirectories(baseDir + "\\" + ffd.cFileName,files);
				}
			}
			else {
				std::string file = ffd.cFileName;					
				std::string::size_type pos = file.find_last_of(".");
				if ( pos != std::string::npos) {
					std::string ending = file.substr(pos);
					std::string fileName = baseDir + "\\" + file;
					ResourceFile rf;
					rf.ending = ending;
					rf.path = baseDir;
					rf.fileName = file;
					rf.size = 0;
					rf.fileTime = FileUtils::getFileTime(fileName);
					rf.fqName = fileName;	
					files.push_back(rf);				
				}				
			}
		}
		while (FindNextFile(hFind, &ffd) != 0);
		FindClose(hFind);
	}
	else {
		LOGC(logERROR,"DeclManager") << "cannot find directory " << baseDir;
	}	
}

void DeclarationManager::debug() {
	Declarations::iterator it = m_Declarations.begin();
	while ( it != m_Declarations.end() ) {
		LOGC(logINFO,"DelcManager") << (*it)->getName() << " : " << translateDeclType((*it)->getType());
		++it;
	}
}

std::string DeclarationManager::translateDeclType(DeclarationType type) {
	std::string ret;
	switch ( type ) {
		case DT_FLOAT : ret.append("DT_FLOAT");break;
		case DT_INT : ret.append("DT_INT");break;
		case DT_VEC2 : ret.append("DT_VEC2");break;
		case DT_COLOR : ret.append("DT_COLOR");break;
		case DT_VEC3 : ret.append("DT_VEC3");break;
		case DT_STRING : ret.append("DT_STRING");break;
		case DT_WORLD : ret.append("DT_WORLD");break;
		case DT_LAYER : ret.append("DT_LAYER");break;
		case DT_TEXTURE : ret.append("DT_TEXTURE");break;
		case DT_RENDER_TARGET : ret.append("DT_RENDER_TARGET");break;
		case DT_SHADER : ret.append("DT_SHADER");break;
		case DT_SCREEN_QUAD : ret.append("DT_SCREEN_QUAD");break;
		case DT_PARTICLESYSTEM : ret.append("DT_PARTICLESYSTEM");break;
		case DT_PARTICLE_AFFECTOR : ret.append("DT_PARTICLE_AFFECTOR");break;
		case DT_SOUND : ret.append("DT_SOUND");break;
		case DT_MENU_ACTION : ret.append("DT_MENU_ACTION");break;
		default : ret.append("UNKNOWN");
	}
	return ret;
}

}
